#coding:UTF-8
from main.models import Image, Outcrop_Collection

"""
Links all unlinked images belonging to a specific excursion to all of its outcrops.
An image belongs to an outcrop if its location is within 200m of the outcrop.
"""

def linkImages(excursion):
    unlinked_images = Image.objects.filter(outcrop=None, excursion=excursion)
    outcrop_list = Outcrop_Collection.objects.filter(excursion=excursion)
    #For every image, check if its within 200m of an existing outcrop,
    #if so, link it to that outcrop
    for image in unlinked_images:
        image_loc = ((image.longitude,  0,  0),  (image.latitude, 0, 0))
        for outcrop in outcrop_list:
            outcrop_loc = ((outcrop.outcrop.longitude,  0,  0), (outcrop.outcrop.latitude, 0, 0))
            if (points2distance(image_loc, outcrop_loc)<=0.200):
                image.outcrop = outcrop.outcrop
                image.save()
                break

#coding:UTF-8
"""
  Python implementation of Haversine formula
  Copyright (C) <2009>  Bartek GÃ³rny <bartek@gorny.edu.pl>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

"""
    This implementation was found at:
    http://www.codecodex.com/wiki/Calculate_Distance_Between_Two_Points_on_a_Globe#Python
    
    All of the code below is from the above website and under the above license unless stated otherwise.
"""
import math

def recalculate_coordinate(val,  _as=None):
    """
        Accepts a coordinate as a tuple (degree, minutes, seconds)
        You can give only one of them (e.g. only minutes as a floating point number) and it will be duly
        recalculated into degrees, minutes and seconds.
        Return value can be specified as 'deg', 'min' or 'sec'; default return value is a proper coordinate tuple.
    """
    deg,  min,  sec = val
    # pass outstanding values from right to left
    min = (min or 0) + int(sec) / 60
    sec = sec % 60
    deg = (deg or 0) + int(min) / 60
    min = min % 60
    # pass decimal part from left to right
    dfrac,  dint = math.modf(deg)
    min = min + dfrac * 60
    deg = dint
    mfrac,  mint = math.modf(min)
    sec = sec + mfrac * 60
    min = mint
    if _as:
        sec = sec + min * 60 + deg * 3600
    if _as == 'sec': return sec
    if _as == 'min': return sec / 60
    if _as == 'deg': return sec / 3600
    return deg,  min,  sec
     

def points2distance(start,  end):
    """
        Calculate distance (in kilometers) between two points given as (long, latt) pairs
        based on Haversine formula (http://en.wikipedia.org/wiki/Haversine_formula).
        Implementation inspired by JavaScript implementation from http://www.movable-type.co.uk/scripts/latlong.html
        Accepts coordinates as tuples (deg, min, sec), but coordinates can be given in any form - e.g.
        can specify only minutes:
        (0, 3133.9333, 0) 
        is interpreted as 
        (52.0, 13.0, 55.998000000008687)
        which, not accidentally, is the lattitude of Warsaw, Poland.
    """
    start_long = math.radians(recalculate_coordinate(start[0],  'deg'))
    start_latt = math.radians(recalculate_coordinate(start[1],  'deg'))
    end_long = math.radians(recalculate_coordinate(end[0],  'deg'))
    end_latt = math.radians(recalculate_coordinate(end[1],  'deg'))
    d_latt = end_latt - start_latt
    d_long = end_long - start_long
    a = math.sin(d_latt/2)**2 + math.cos(start_latt) * math.cos(end_latt) * math.sin(d_long/2)**2
    c = 2 * math.asin(math.sqrt(a))
    return 6371 * c